/**
 * @file		uart.c
 * @brief		UART
 * @note		None
 * @attention	None
 * 
 * <B><I>ALL RIGHTS RESERVED, COPYRIGHT&copy; SOCIONEXT INCORPORATED 2016</I></B>
 */

/* Standard includes. */
#include <stdio.h>
#include <string.h>

#include "dd_arm.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"

#include "user_custom.h"

#include "dd_pmc_uart.h"
#include "uart.h"

#include "ipcu.h"

// DMA channel
#define D_UART_RECEIVE_DMA_CH			(1)
#define D_UART_SEND_DMA_CH				(2)

// UART status
#define D_UART_STATUS_STOP				(0)		// stop.
#define D_UART_STATUS_RUNNING			(1)		// running.

// Send data buffer size max.
#define D_UART_SEND_BUFFER_SIZE_MAX		(256)
// Receive data buffer size max.
#define D_UART_RECEIVE_BUFFER_SIZE_MAX	(256)


// Notification enable.
static volatile int iUARTSendNotificationEnable = 0;
static volatile int iUARTReceiveNotificationEnable = 0;

// Buffer data.
static volatile T_UART_SEND_BUFFER xSendBuffer = { 0, 0 };
static volatile T_UART_RECEIVE_BUFFER xReceiveBuffer = { 0, 0, 0, 0 };

// UART Send data buffer.
static volatile char cUartSendBuffer[D_UART_SEND_BUFFER_SIZE_MAX];
// UART Receive data buffer.
static volatile char cUartReceiveBuffer[D_UART_RECEIVE_BUFFER_SIZE_MAX];

// status (0:stop, 1:running)
static volatile int iUARTSendStatus = D_UART_STATUS_STOP;
static volatile int iUARTReceiveStatus = D_UART_STATUS_STOP;

// Read data.
static char cReadDataBuffer[256];
static int iReadDataCount;

// Local function.
static void prvUARTSendCallback(void);
static void prvUARTReceiveCallback(int* result);


/**
UART Send callback function.
*/
static void prvUARTSendCallback()
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	BaseType_t		xHigherPriorityTaskWoken, xResult;
	unsigned long	ulData[3];
#endif
	
	if (iUARTSendStatus == D_UART_STATUS_RUNNING) {
		// Set status : stop.
		iUARTSendStatus = D_UART_STATUS_STOP;
		
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
		// Notification.
		if (iUARTSendNotificationEnable) {
			xHigherPriorityTaskWoken = pdFALSE;
			
			// Set UART Send Notify data
			ulData[0] = 0x02000003;
			ulData[1] = 0;
			ulData[2] = 0;
			vIPCUSetNotifyCommand(ulData, 4);
			
			// Set Event Flag : UART Send Notification.
			xResult = xEventGroupSetBitsFromISR(xNotifyEventFlag, (1 << NFY_UART_SEND), &xHigherPriorityTaskWoken);
			if (xResult == pdPASS) {
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
			}
		}
#endif
	}
}

/**
UART Receive callback function.
*/
static void prvUARTReceiveCallback(int* result)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	BaseType_t		xHigherPriorityTaskWoken, xResult;
	unsigned long	ulData[3];
	
	if (iUARTReceiveStatus == D_UART_STATUS_RUNNING) {
		// It stores the received data in the buffer.
		*((char*)(xReceiveBuffer.address + xReceiveBuffer.pointer)) = (char)*result;
		
		if (xReceiveBuffer.size > xReceiveBuffer.pointer) {
			// Next point.
			xReceiveBuffer.pointer++;
		}
		else {
			switch (xReceiveBuffer.overlap) {
				case 1:			// cyclic.
					// pointer clear (Set top position).
					xReceiveBuffer.pointer = 0;
					break;
				case 2:			// same position.
					break;
				default:		// no-overlap.
					// Set status : stop.
					iUARTReceiveStatus = D_UART_STATUS_STOP;
					// Notification disable.
					iUARTReceiveNotificationEnable = 0;
					break;
			}
		}
		
		// Notification.
		if (iUARTReceiveNotificationEnable) {
			xHigherPriorityTaskWoken = pdFALSE;
			
			// Set UART Receive Notify data
			ulData[0] = 0x02000004;
			ulData[1] = 0;
			ulData[2] = 0;
			vIPCUSetNotifyCommand(ulData, 3);
			
			// Set Event Flag : UART Receive Notification.
			xResult = xEventGroupSetBitsFromISR(xNotifyEventFlag, (1 << NFY_UART_RECEIVE), &xHigherPriorityTaskWoken);
			if (xResult == pdPASS) {
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
			}
		}
	}
#endif
}

/**
UART Initialization.
*/
void vUARTInit(void)
{
	// Global data clear.
	iReadDataCount = 0;
	memset(&cReadDataBuffer[0], 0, 256);
	
	T_DD_PMC_UART_MNG	ctrl;
	
	ctrl.baud_rate			= E_DD_PMC_UART_BAUD_RATE_115200;
	ctrl.receive_buff_addr	= (void*)cUartReceiveBuffer;
	ctrl.receive_buff_size	= D_UART_RECEIVE_BUFFER_SIZE_MAX;
	ctrl.send_buff_addr		= (void*)cUartSendBuffer;
	ctrl.send_buff_size		= D_UART_SEND_BUFFER_SIZE_MAX;
	ctrl.auto_echo			= E_DD_PMC_UART_AUTO_ECHO_ON;
	ctrl.save_buff			= E_DD_PMC_UART_SAVE2BUFFER_EN;

	ctrl.mode				= E_DD_PMC_UART_MODE_NORMAL;
	ctrl.bit_direction		= E_DD_PMC_UART_BIT_DIR_LSB_FIRST;
	ctrl.stop_bit_length	= E_DD_PMC_UART_STOP_BIT_1;
	ctrl.parity_bit			= E_DD_PMC_UART_PARITY_BIT_NONE;
	ctrl.data_length		= E_DD_PMC_UART_DATA_LENGTH_8;
	ctrl.inv_nrz			= 0;
	ctrl.fifo_ctrl			= NULL;
	Dd_PMC_UART_Ctrl(&ctrl);
	
	// UART Start.
	Dd_PMC_UART_Start(prvUARTSendCallback, prvUARTReceiveCallback);
}

/**
UART Send start.
@retval  0 : success
@retval  1 : patameter error.
@retval  3 : function error.
*/
int iUARTSendStart(void)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	int iRes	= 0;
	int iCount	= 0;
	
	if ((xSendBuffer.address == 0) || (xSendBuffer.size == 0)) {
		// failure : no-buffer.
		return 1;
	}
	
#if 0
	// status check
	if (iUARTSendStatus == D_UART_STATUS_RUNNING) {
		// failure.
		return 2;
	}
#endif
	
	// Set status : running.
	iUARTSendStatus = D_UART_STATUS_RUNNING;
	
	// Set data.
	for (iCount = 0; iCount < xSendBuffer.size; iCount++) {
		Dd_PMC_UART_Set_Str((char)*((char*)(xSendBuffer.address + iCount)));
	}
	
	// Send.
	iRes = Dd_PMC_UART_Put_Str();
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
#endif	// UART_TYPE
	
	// success.
	return 0;
}

/**
UART Receive start.
@retval  0 : success
@retval  2 : status error.
*/
int iUARTReceiveStart(void)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	// status check
	if (iUARTReceiveStatus == D_UART_STATUS_RUNNING) {
		// status error.
		return 2;
	}
	
	// Set status : running.
	iUARTReceiveStatus = D_UART_STATUS_RUNNING;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
UART Receive stop.
@retval  0 : success
@retval  2 : status error.
*/
int iUARTReceiveStop(void)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	// status check
	if (iUARTReceiveStatus == D_UART_STATUS_STOP) {
		// status error.
		return 2;
	}
	
	// Set status : stop.
	iUARTReceiveStatus = D_UART_STATUS_STOP;
	
	// Notification disable.
	iUARTReceiveNotificationEnable = 0;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
set UART Send notification.
@param [in]	enable		notification enable.<br>
						0:disable, 1:enable
@retval  0 : success
@retval  1 : parameter error
*/
int iUARTSendNotification(int enable)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	if ((enable != 0) && (enable != 1)) {
		// parameter error.
		return 1;
	}
	
	// Set notification.
	iUARTSendNotificationEnable = enable;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
set UART Receive notification.
@param [in]	enable		notification enable.<br>
						0:disable, 1:enable
@retval  0 : success
@retval  1 : parameter error
*/
int iUARTReceiveNotification(int enable)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	if ((enable != 0) && (enable != 1)) {
		// parameter error.
		return 1;
	}
	
	// Set notification.
	iUARTReceiveNotificationEnable = enable;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
set UART input buffer for send.
@param [in]	buff		Send buffer data.
@retval  0 : success
@retval  1 : parameter error
*/
int iUARTSetSendBuffer(T_UART_SEND_BUFFER const* const buff)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	if (buff == 0) {
		// parameter error.
		return 1;
	}
	
	xSendBuffer = *buff;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
set UART input buffer for receive.
@param [in]	buff		Receive buffer data.
@retval  0 : success
@retval  1 : parameter error
*/
int iUARTSetReceiveBuffer(T_UART_RECEIVE_BUFFER const* const buff)
{
#if (UART_TYPE==UART_TYPE_REQUEST_CA7)
	if (buff == 0) {
		// parameter error.
		return 1;
	}
	
	xReceiveBuffer = *buff;
#endif	// UART_TYPE
	
	// normal end.
	return 0;
}

/**
UART Output Terminal.
@param [in]	pointer		send data top.
@param [in]	size		send size.
@retval  0 : success
@retval  1 : failure.
*/
int iUARTOutputTerminal(char* pointer, unsigned long size)
{
#if (UART_TYPE==UART_TYPE_TERMINAL_CMD)
	int iRes	= 0;
	int iCount	= 0;
	
#if 0
	// status check
	if (iUARTSendStatus == D_UART_STATUS_RUNNING) {
		// failure.
		return 1;
	}
#endif
	
	// Set status : running.
	iUARTSendStatus = D_UART_STATUS_RUNNING;
	
	// Set data.
	for (iCount = 0; iCount < size; iCount++) {
		Dd_PMC_UART_Set_Str((char)*(pointer + iCount));
	}
	
	// Send.
	iRes = Dd_PMC_UART_Put_Str();
	if (iRes != D_DDIM_OK) {
		// failure.
		return 1;
	}
#endif	// UART_TYPE
	
	// success.
	return 0;
}

/**
Read the input data from the terminal.
@param [out]	pointer		read data top.
@retval  0 : success
@retval  1 : failure.
*/
int iUARTReadTerminal(char* pointer)
{
#if (UART_TYPE==UART_TYPE_TERMINAL_CMD)
	INT32 iResult;
	
	while (iReadDataCount < 256) {
		iResult = Dd_PMC_UART_Get_Char(&cReadDataBuffer[iReadDataCount]);
		if (iResult == D_DDIM_OK) {
			// echo data output.
			Dd_PMC_UART_Put_Str();
			
			if (cReadDataBuffer[iReadDataCount] == '\n') {
				// If the read character is a newline code.
				break;
			}
			
			// Read data count increment.
			iReadDataCount++;
		}
		else {
			// No data or Error.
			return 1;
		}
	}
	
	// Set output paramter.
	memcpy(pointer, &cReadDataBuffer[0], iReadDataCount);
	
	// Data clear.
	iReadDataCount = 0;
	memset(&cReadDataBuffer[0], 0, 256);
#endif	// UART_TYPE
	
	return 0;
}
